/*
 * @LastEditors: Darth_Eternalfaith
 * @LastEditTime: 2022-06-26 23:22:57
 */
/** 提供一点点3d数学支持的js文件
 * 如无另外注释, 在这个文件下的所有3d坐标系都应为 右手坐标系
 */

import { Iterator__MyVirtual } from "../basics/Basics.js";
import { Stepper } from "../basics/math_ex.js";
import { Matrix2x2 } from "./Math2d.js";


var sqrt=Math.sqrt,
    cos = Math.cos,
    sin = Math.sin;

/**
 * 3d 向量
 */
class Vector3{
    /** 
     * @param {Number|Array} x x坐标; 重载(array)作为 xyz 坐标数组
     * @param {Number} [y] y坐标; 重载2无用
     * @param {Number} [z] z坐标; 重载2无用
     */
    constructor(x,y,z){
        if(Array.isArray(x)){
            this.x=x[0];
            this.y=x[1];
            this.z=x[2];
        }else{
            this.x=x||0;
            this.y=y||0;
            this.z=z||0;
        }
    }
    /** 转换成数组
     * @returns {Array} 返回长度为3的数组
     */
    toArray(){
        return [this.x,this.y,this.z];
    }
    /** 转换成数组
     * @param {Vector3} v3 目标向量
     * @returns {Array} 返回长度为3的数组
     */
    static toArray(v3){
        return [v3.x,v3.y,v3.z];
    }
    /** 拷贝函数
     * @param {Vecotr3} v3 拷贝目标
     * @return {Vector3} 返回一个新的向量
     */
    static copy(v3){
        return new Vector3(v3.x,v3.y,v3.z);
    }
    /** 拷贝函数
     * @return {Vector3} 返回一个新的向量
     */
    copy(){
        return Vector3.copy(this);
    }
    /**向量和
     * @param {Vector3} a 向量a
     * @param {Vector3} b 向量b
     * @returns {Vector3} 返回新的向量
     */
    static sum(a,b){
        return new Vector3(a.x+b.x,a.y+b.y,a.z+b.z);
    }
    /**向量差
     * @param {Vector3} a 被减
     * @param {Vector3} b 减
     * @returns {Vector3} 返回新的向量
     */
    static dif(a,b){
        return new Vector3(a.x-b.x,a.y-b.y,a.z-b.z);
    }
    /** 数字乘向量
     * @param {Number} n 数字
     * @param {Vector3} v 向量
     * @returns {Vector3} 返回新向量
     */
    static np(n,v) {
        return new Vector3(v.x * n, v.y * n, v.z * n);
    }
    
    /** 向量内积 (a · b)
     * @param {Vector2} a 向量a
     * @param {Vector2} b 向量b
     * @returns{Number} 
     */
    static dot(a,b){return a.x*b.x+a.y*b.y+a.z*b.z;}
    /** 向量外积 (a × b)
     * @param {Vector2} a 向量a
     * @param {Vector2} b 向量b
     */
    static cross(a,b){
        return new Vector3(
            a.y*b.z-a.z*b.y,
            a.z*b.x-a.x*b.z,
            a.x*b.y-a.y*b.x
        );
    }
    /** 归零
     * @param {Vector3} v3  操作的对象
     * @returns {Vector3} 返回操作的对象
     */
    static re_Zero(v3){
        v3.x=v3.y=v3.z=0;
        return v3;
    }
    /** 归零 */
    re_Zero(){
        this.x=this.y=this.z=0;
        return this;
    }
    /** 取模长
     * @param {Vector3} 目标向量
     * @returns {Number} 取模长 
     */
    static get_Meg(v3){
        return sqrt(v3.x * v3.x + v3.y * v3.y + v3.z * v3.z);
    }
    /** 取模长
     * @returns {Number} 取模长 
     */
    get_Meg(){
        return sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
    }
    /** 取反
     * @param {Vector3} v3 目标向量
     * @return {Vector3} 返回新的向量
     */
    static create_instead(v3){
        return new Vector3(-v3.x,-v3.y,-v3.z);
    }
    /** 取反
     * @return {Vector3} 返回新的向量
     */
    create_instead(){
        return new Vector3(-this.x,-this.y,-this.z);
    }
    /** 取反
     * @return {vector3} 返回当前
     */
    instead(){
        this.x=-this.x;this.y=-this.y;this.z=-this.z;
        return this;
    }
    /** 创建标准化向量
     * @param {Vector3} v3 目标向量
     * @return {Vector3} 返回新向量
     */
    static create_Normalization(v3){
        var m_sp=Vector3.get_Meg(v3);
        if(m_sp>0){
            m_sp=1/m_sp;
            return new Vector3(v3.x*m_sp,v3.y*m_sp,v3.z*m_sp);
        }
        return new Vector3();
    }
    /** 创建标准化向量
     * @return {Vector3} 返回新向量
     */
    create_Normalization(){
        var v3=this;
        var m_sp=Vector3.get_Meg(v3);
        if(m_sp>0){
            m_sp=1/m_sp;
            return new Vector3(v3.x*m_sp,v3.y*m_sp,v3.z*m_sp);
        }
        return new Vector3();
    }
    /** 标准化向量
     * @return {Vector3} 返回当前向量
     */
    normalization(){
        var m_sp=Vector3.get_Meg(this);
        if(m_sp>0){
            m_sp=1/m_sp;
            this.x*=m_sp;
            this.y*=m_sp;
            this.z*=m_sp;
        }
        return this;
    }
}

/** 3x3矩阵的迭代器, 顺序: ma1 >> mb1 >> mc1 >> ma2 >> mb2 >> mc2 >> ma3 >> mb3 >> mc3*/
class Iterator__Matrix3x3 extends Iterator__MyVirtual{
    /** 
     * @param {Matrix3x3} m 
     */
    constructor(m){
        super(m);
        /**@type {Matrix3x3} */
        this.data;
        this.i=0;
        this.j=0;
    }
    init(){
        this.i=0;
        this.j=0;
    }
    next(){
        ++this.i;
        if(this.i>2){
            this.i=0;
            ++this.j;
        }
    }
    is_NotEnd(){
        return this.j>2;
    }
    get_Now(){
        return this.data[this.j][this.i];
    }
    /** 存储到当前位置
     * @param {Number} val 
     */
    set_Now(val){
        this.data[this.j][this.i]=val;
    }
}

/*!    a   b   c
 *  1 ma1 mb1 mc1
 *  2 ma2 mb2 mc2
 *  3 ma3 mb3 mc3
 * 
 *  [
 *  [ma1,mb1,mc1],
 *  [ma2,mb2,mc2],
 *  [ma3,mb3,mc3],
 *  ]
 */

/** 3x3 矩阵 */
class Matrix3x3{

    /**
     * @param {Number[]|Number[][]} data 数据来源,3x3二维数组或长度为9的数组
     * @param {Boolean} [flag_uncopy] 是否要不需要拷贝数据, 如果不想拷贝数据 data必须是3*3数组; 默认为需要拷贝(false)
     */
    constructor(data,flag_uncopy){
        /**@type {Number[][]} 使用二维数组存储矩阵数据*/
        this._data;
        if(flag_uncopy){
            this._data=data
        }else{
            this._data=Matrix3x3.arr_To_MaatrixData(data);
        }

        /** @type {Stepper} x 缓存 步进器 */
        this._p_x=null;
        /** @type {Stepper} y 缓存 步进器 */
        this._p_y=null;

        /**@type {Matrix3x3} 缓存 标准伴随矩阵 */
        this._m_adjugate=null;
        /**@type {Matrix3x3} 缓存 逆矩阵 */
        this._m_inverse=null;
    }
    get data(){
        return this._data;
    }
    /** 创建单位矩阵
     * @returns {Matrix3x3} 返回一个单位矩阵
     */
    static create__identity(){
        return new Matrix3x3([
            [1,0,0],
            [0,1,0],
            [0,0,1]
        ],true);
    }
    
    /** 创建单位矩阵
     * @returns {Matrix3x3} 返回一个单位矩阵
     */
    static create_data__identity(){
        return [
            [1,0,0],
            [0,1,0],
            [0,0,1]
        ];
    }
    /**@type {Stepper} 用于计算时取下标的步进器 */
    get p_y(){
        if(this._p_y===null){
            this._p_y=new Stepper(2,0,0);
        }
        return this._p_y;
    }
    /**@type {Stepper} 用于计算时取下标的步进器 */
    get p_x(){
        if(this._p_x===null){
            this._p_x=new Stepper(2,0,0);
        }
        return this._p_x;
    }

    get ma1(){return this._data[1][1];}   get mb1(){return this._data[1][2];}   get mc1(){return this._data[1][3];}
    get ma2(){return this._data[2][1];}   get mb2(){return this._data[2][2];}   get mc2(){return this._data[2][3];}
    get ma3(){return this._data[3][1];}   get mb3(){return this._data[3][2];}   get mc3(){return this._data[3][3];}
    
    set ma1(val){this.refresh_Cache();return this._data[1][1]=val;}   set mb1(val){this.refresh_Cache();return this._data[1][2]=val;}   set mc1(val){this.refresh_Cache();return this._data[1][3]=val;}
    set ma2(val){this.refresh_Cache();return this._data[2][1]=val;}   set mb2(val){this.refresh_Cache();return this._data[2][2]=val;}   set mc2(val){this.refresh_Cache();return this._data[2][3]=val;}
    set ma3(val){this.refresh_Cache();return this._data[3][1]=val;}   set mb3(val){this.refresh_Cache();return this._data[3][2]=val;}   set mc3(val){this.refresh_Cache();return this._data[3][3]=val;}
    
    /** 存数据
     * @param {Number[][]} data 数据来源 二维数组 空位补0
     * @return {Matrix3x3} 返回当前对象引用
     */
    set_Data(data){
        this.refresh_Cache();
        var _data=this._data;
        for(i=2;i>=0;--i){
            _data[i]=new Array(3);
            for(j=2;j>=0;--j){
                _data[i][j]=(data[i]&&data[i][j])||0;
            }
        }
        return this;
    }
    /** 刷新只读属性 */
    refresh_Cache(){
        this._det=null;
    }
    /** 拷贝函数
     * @param {Matrix3x3} m 拷贝的对象
     * @return {Matrix3x3} 返回一个新矩阵
     */
    static copy(m){
        return new Matrix3x3(m._data);
    }
    
    /** 拷贝函数
     * @return {Matrix3x3} 返回一个新矩阵
     */
    copy(){
        return new Matrix3x3(this._data);
    }
    
    /** 转换成数字数组
     * @returns {Number[]} 返回数字数组
     */
    toArray(){
        var rtn=[];
        var i;
        for(i=0;i<this.data.length;++i){
            rtn.push.apply(rtn,this.data[i]);
        }
        return rtn;
    }

    /** @type {Number} 矩阵行列式 */
    get det(){
        if(this._det===null||this._det===undefined){
            this._det=Matrix3x3.calc_Det(this);
        }
        return this._det;
    }
    
    /** 当前矩阵右乘矩阵
     * @param {Matrix3x3} m 左乘入的矩阵
     * @return {Matrix3x3} 返回当前对象的引用
     */
    multiplication(m){
        this.refresh_Cache();
        matrix_Multiplication(this._data,m,2);
        return this;
    }

    /** 当前矩阵左乘矩阵
     * @param {Matrix3x3} m 左乘入的矩阵
     * @return {Matrix3x3} 返回当前对象的引用
     */
    multiplication_before(m){
        this.refresh_Cache();
        matrix_Multiplication(m,this._data,2);
        return this;
    }

    /** 获取标准伴随矩阵
     *  @type {Matrix3x3} 获取当前矩阵的标准伴随矩阵的引用
     */
    get_Adjugate(){
        if(!this._m_adjugate){
            this._m_adjugate=new Matrix3x3(Matrix3x3.create_Data__AdjugateMatrix(this),true);
        }
        return this._m_adjugate;
    }

    /** 获取逆矩阵
     *  @type {Matrix3x3} 获取当前矩阵的逆矩阵的引用
     * @returns 
     */
    get_Inverse(){
        if(!this._m_inverse){
            this._m_inverse=new Matrix3x3(Matrix3x3.create_Data__Inverse(this),true);
        }
        return this._m_inverse;
    }

    /** 计算矩阵行列式
     * @param {Matrix3x3|Number[][]|Number[]} md 数据来源
     * @return {Number} 返回一个标量
     */
    static calc_Det(md){
        var d=md;
        if(d instanceof Matrix3x3){
            d=md.data;
        }else{
            d=Matrix3x3.arr_To_MaatrixData(md);
        }
        return  d[0][0] * (d[1][1]*d[2][2] - d[2][1]*d[1][2])+
                d[1][0] * (d[2][1]*d[0][2] - d[0][1]*d[2][2])+
                d[2][0] * (d[1][2]*d[0][1] - d[1][1]*d[0][2]);        
    }

    /** 创建逆矩阵 
     * @param {Matrix3x3|Number[][]} m 目标矩阵 
     * @returns {Number[][]} 返回矩阵数据 当这是奇异矩阵时返回一个单位矩阵
     */
    static create_Data__Inverse (m){
        var det;
        if(m instanceof Matrix3x3){
            det=m.det;
        }else{
            det=Matrix3x3.calc_Det(m)
        }
        var m=Matrix3x3.create_Data__AdjugateMatrix(m);
        if(det===0){
            console.warn("this is a Singular matrix");
            return [[1,0,0],[0,1,0],[0,0,1]];
        }
        var sp_det=1/det;
        for(var i = 2;i>=0;--i){
            for(var j = 2;j>=0;--j){
                m[i][j]*=sp_det;
            }
        }
        return m;
    }
    
    /** 创建标准伴随矩阵
     * @param {Matrix3x3|Number[][]} md 目标矩阵
     * @returns {Number[][]} 返回矩阵数据
     */
    static create_Data__AdjugateMatrix(m){
        /**@type {Number[][]} 数据 */
        var d=m;
        if(d.data){
            d=d.data;
        }
        var rtn=Matrix3x3.create_data__identity();
        var i,j;
        var f=-1;
        for(i=d.length-1;i>=0;--i){
            for(j=d.length-1;j>=0;--j){
                f*=-1;
                rtn[i][j]=f*Matrix3x3.calc_Cofactor(d,i,j);
            }
        }
        return rtn;
    }

    /** 求余子式
     * @param {Matrix3x3|Number[][]} m 目标矩阵
     * @param {Number} dx x下标
     * @param {Number} dy y下标
     * @return {Number} 返回余子式
     */
    static calc_Cofactor(m,dx,dy){
        var d=m;
        if(d instanceof Matrix3x3){
            d=d.data;
        }
        var temp=[];
        var i,j;
        for(i=0;i<3;++i){
            if(i===dx)continue;
            for(j=0;j<3;++j){
                if(j===dy)continue;
                temp.push((d[i]&&d[i][j])||0);
            }
        }
        return Matrix2x2.calc_Det(temp);
    }

    /** 把Number[] 或 Number[][]转换成 3x3矩阵的data使用的样子 
     * @param {Number[]|Number[][]} data 传入的数据,空位会补0
     * @return {Number[][]} 返回新的矩阵数据
     */
    static arr_To_MaatrixData(data){
        if(Array.isArray(data)){
            if(Array.isArray(data[0])){
                var i,j;
                var _data=new Array(i);
                for(i=2;i>=0;--i){
                    _data[i]=new Array(3);
                    for(j=2;j>=0;--j){
                        _data[i][j]=(data[i]&&data[i][j])||0;
                    }
                }
                return _data;
            }
            else{
                return create_Data__ByNumberArray(data,3,3);
            }
        }
        else{
            return Matrix3x3.create_data__identity();
        }
        
    }
    /** 矩阵转置, 会将传入的data转置并返回
     * @param {Number[][]} data 原矩阵数据
     * @return {Number[][]} 返回data
     */
    static transpose_Data(data){
        var temp=0,
            i,j;
        for(i=data.length-1;i>=0;--i){
            for(j=i-1;j>=0;--j){
                temp=data[i][j];
                data[i][j]=data[j][i];
                data[j][i]=temp;
            }
        }
        return data;
    }
}

/**
 * 静态类，存放创建矩阵的各种方法, 默认右乘矩阵
 */
class Creater_Matrix3x3Data{
    /** 旋转矩阵
     * @param {Number} axis 旋转中心轴  [ 1:x, 2:y, 3:z ]
     * @param {Number} theta 旋转弧度
     * @return {Number[][]} 
     */
    static rotate(axis,theta){
        var s=sin(theta),
            c=cos(theta);
        switch(axis){
            case 1:
                // 绕x轴旋转
                return [[1, 0, 0],
                        [0, c, s],
                        [0,-s, c]];
            break;
            case 2:
                // 绕y轴旋转
                return [[c, 0,-s],
                        [0, 1, 0],
                        [s, 0, c]];
            break;
            case 3:
                // 绕z轴旋转
                return [[ c, s, 0],
                        [-s, c, 0],
                        [ 0, 0, 1]];
            break;
        }
    }
    /** 缩放矩阵
     * @param {*} x 
     * @param {*} y 
     * @param {*} z 
     */
    scale(x, y, z){
        return [[x,0,0],
                [0,y,0],
                [0,0,z]]
    }
}

/** 矩阵乘法
 * @param {Number[][]} m1 左乘矩阵
 * @param {Number[][]} m2 右乘矩阵
 * @param {Number} [set_flag] 直接修改矩阵数据, 可选值 {1,2} 存储到m1或m2
 * @return {Number[][]} 返回矩阵数据 如果有set_flag将返回那个被修改的数据
 */
function matrix_Multiplication(m1,m2,set_flag){
    var d1=m1,
        d2=m2;
    if((d1.length&&d2.length&&d1[0].length&&d2[0].length)&&(d1.length!==d2[0].length)){
        throw new TypeError(`There matrix size make there can not run multiplication ! w:${d2.length&&d2[0].length}; h:${d1.length}`);
    }
    var rtn;
    if(set_flag&&arguments[set_flag-1]){
        rtn=arguments[set_flag-1];
    }else{
        rtn=[];
    }

    var i,j,k,temp;
    for(i=d1.length-1;i>=0;--i){
        if(!rtn[i]){
            rtn[i]=new Array(d2[0].length);
        }
        for(j=d2.length-1;j>=0;--j){
            temp=0;
            for(k=d2.length-1;k>=0;--k){
                temp+=d1[i][k]*d2[k][j];
            }
            rtn[i][j]=temp;
        }
    }
    return rtn;
}

/** 由 Number数组,宽高 创建矩阵数据 
 * @param {Number[]} data 数据 长度不足会补0
 * @param {Number} w 宽
 * @param {Number} h 高
 * @returns {Number[][]} 返回二维数组
 */
function create_Data__ByNumberArray(data,w,h){
    if(w===0||h===0){
        throw new TypeError("this w|h can not be a matrix!");
    }
    var rtn=new Array(h);
    var i=0,j=0,k=0;
    do{
        rtn[i]=new Array(w);
        for(j=0;j<h;++j,++k){
            rtn[i][j]=(data&&data[k])||0;
        }
        ++i;
    }while(i<h);
    return rtn;
}



export {
    Vector3,
    Matrix3x3
}